package aor2.cpu.blockcontrol;

//import java.io.IOException;
//import java.util.logging.FileHandler;
//import java.util.logging.SimpleFormatter;

import aor2.components.Register;
import aor2.components.Signal;
import aor2.cpu.ClockCPU;
import aor2.cpu.InternBus;
import aor2.cpu.blockinterface.Arbitrator;
import aor2.cpu.blockinterface.BusOvertake;
import aor2.cpu.blockinterface.InterfaceBlock;
import aor2.cpu.blockinterface.SynchronizationBlock;
import aor2.cpu.blockinterrupt.InterruptBlock;
import aor2.cpu.blockregisters.GPRegisters;

public abstract class ControlUnitMicroProg {

	protected Signal ld, run;
	protected MicroMemory uMem;
	protected Register uCnt, cw;
	protected long kmop, kmadr1, kmadr2, ba;
	protected Signal branch, brop, bradr1, bradr2, bruncnd;
	protected MicroInstruction uInstr;
	private boolean clkFstPhase;
	private ClockCPU clock;
	private boolean newInstructionFlag;

	// private static FileHandler hand;
	// private static Logger log;
	// static{
	// //log = Logger.getLogger("step");
	// try {
	// hand = new FileHandler("loggingInfo.txt",true);//append
	// hand.setFormatter(new SimpleFormatter());
	// ////log.addHandler(hand);
	// } catch (SecurityException e) {
	// e.printStackTrace();
	// } catch (IOException e) {
	// e.printStackTrace();
	// }
	// }

	protected ControlUnitMicroProg(MicroMemory uMem, Register uCnt, Register cw) {
		super();
		newInstructionFlag = false;
		this.uMem = uMem;
		this.uCnt = uCnt;
		this.cw = cw;
		kmop = kmadr1 = kmadr2 = ba = 0;
		ConditionSignalSet condss = ConditionSignalSet.getInstance();
		run = condss.get("run");
		ld = new Signal(false);
		ControlSignalSet ctrlss = ControlSignalSet.getInstance();
		branch = ctrlss.get("branch");
		brop = ctrlss.get("brop");
		bradr1 = ctrlss.get("braadr1");
		bradr2 = ctrlss.get("braadr2");
		bruncnd = ctrlss.get("bruncnd");
		clkFstPhase = true;
		clock = ClockCPU.getInstance();
	}

	public MicroMemory getuMem() {
		return uMem;
	}

	public Register getuCnt() {
		return uCnt;
	}

	public Register getCw() {
		return cw;
	}

	public boolean ldActive() {
		return ld.isActive();
	}

	public long outKMOP() {
		return kmop;
	}

	public long outKMADR1() {
		return kmadr1;
	}

	public long outKMADR2() {
		return kmadr2;
	}

	protected void readCW() {
		cw.setState(uMem.getMicroInstruction(uCnt.getState()).getCode());
	}

	public MicroInstruction getuInstr() {
		return uInstr;
	}

	public abstract long getBranchAddress();

	protected abstract void setBranchAddress();

	protected abstract void generateBrLd();

	protected abstract void nextuCnt();

	protected abstract void prevuCnt();

	public abstract String phaseInfo();

	/**
	 * when IR0in this function is called to set KMs outputs
	 */
	public abstract void generateKMsignals();

	/**
	 * generates next value for mPC (uCNT) and proceeds to the next step
	 */
	public void next() {
		// System.out.println(this.toString());

		// log.info(this.toString());
		clock.nextClkPart();
		// System.out.println(GPRegisters.info());
		if (clkFstPhase) {
			if (uInstr != null) {
				MicroInstruction prevuInstr = uInstr;
				nextuCnt();
				InternBus.clear();
				prevuInstr.clearSignals();
				readCW();
				newInstructionFlag = uCnt.getState() == 0;
			}
			uInstr = uMem.getMicroInstruction(uCnt.getState());
			uInstr.start();
			// InterfaceBlock.getInstance().next();
			BusOvertake.getInstance().next();
			Arbitrator.getInstance().next();
			SynchronizationBlock.getInstance().next();

			InterfaceBlock.getInstance().next();

			InterruptBlock.getInstance().next();
			generateBrLd();
			setBranchAddress();
		} else {
			newInstructionFlag = false;
			// InterfaceBlock.getInstance().next();
			BusOvertake.getInstance().next();
			Arbitrator.getInstance().next();
			SynchronizationBlock.getInstance().next();

			InterfaceBlock.getInstance().next();// XXX videti da li je ovde ili
												// pre

			InterruptBlock.getInstance().next();
			uInstr.updateStates();
		}
                
		clkFstPhase = !clkFstPhase;
	}

	public void previousClk() {
		clock.previousClkPart();
		// System.out.println(GPRegisters.info());
		clkFstPhase = true;
		if (uInstr != null) {
			MicroInstruction prevuInstr = uInstr;
			prevuCnt();
			InternBus.clear();
			prevuInstr.clearSignals();
			readCW();
			newInstructionFlag = uCnt.getState() == 0;
		}
		uInstr = uMem.getMicroInstruction(uCnt.getState());
		uInstr.start();
		// InterfaceBlock.getInstance().next();
		BusOvertake.getInstance().previousClk();
		Arbitrator.getInstance().previousClk();
		SynchronizationBlock.getInstance().previousClk();

		InterfaceBlock.getInstance().previousClk();

		InterruptBlock.getInstance().previousClk();
		generateBrLd();
		setBranchAddress();
	}

	public void nextInstruction() {
		do {
			next();// XXX treba li jos nesto posto je fixme za clock?
		} while (!(newInstructionFlag));
	}

	@Override
	public String toString() {
		return clock.toString() + "\n" + "ControlUnitMicroProg [\nld=" + ld
				+ ", run=" + run + ", \nuCnt=" + uCnt + ", cw=" + cw
				+ ", \nkmop=" + kmop + ", kmadr1=" + kmadr1 + ", kmadr2="
				+ kmadr2 + ", ba=" + ba + ", \nbranch=" + branch + ", \nbrop="
				+ brop + ", \nbradr1=" + bradr1 + ", \nbradr2=" + bradr2
				+ ", \nbruncnd=" + bruncnd + ", \n\nuInstr=" + uInstr + "\n]"
				+ "\n\nPC: " + Long.toHexString(GPRegisters.R7.getState())
				+ "\n" + InternBus.info() + "\n========================\n\n";
	}

}
